package com.gitee.wsl.net.ssdp

import com.gitee.wsl.net.ssdp.description.IDescriptionService
import com.gitee.wsl.net.ssdp.device.Device
import com.gitee.wsl.net.ssdp.device.Icon
import com.gitee.wsl.net.ssdp.message.SsdpMessage
import java.net.InetAddress

/**
 * Interface of UPnP ControlPoint.
 * @doc "https://openconnectivity.org/upnp-specs/UPnP-arch-DeviceArchitecture-v2.0-20200417.pdf" p.9
 */
interface IControlPoint {

    val notificationService: INotificationService
    val descriptionService: IDescriptionService

    /** List of discovered devices */
    val deviceList: List<Device>

    /**
     * Do initialize. Must be call before use.
     *
     * Once initialized, reinitialization is impossible.
     * If reinitialization is required, such as changing the interface, start over from creating an instance.
     * Also, be sure to call [terminate] when ending.
     */
    fun initialize()

    /**
     * Do terminate.
     *
     * If it is in operation, [stop] processing is performed.
     * The instance can not be used after terminate.
     */
    fun terminate()

    /**
     * Start the control point.
     *
     * It is not possible to execute network related processing before calling this method.
     * Nothing is done if it is already started.
     * Even after it has been started once, it can be started again if it is after stop.
     *
     * @see initialize
     */
    fun start()

    /**
     * Stop the control point.
     *
     * Nothing happens if it is not started or already stopped.
     * The discovered [Device]s are treated as lost and must be removed.
     *
     * @see [onLost]
     */
    fun stop()

    /**
     * Clear the discovered device list.
     * Devices held at the time of the call must be notified as lost.
     *
     * @see [onLost]
     */
    fun clearDeviceList()

    /**
     * Send Search packet with ST field [st]. If not provided the default "ssdp:all" is used.
     */
    fun search(target: String? = "ssdp:all", inetAddress: InetAddress? = null)

    /**
     * Set a filter [predicate] to judge whether to accept or ignore SsdpMessage. If not provided -  accept all.
     */
    fun setSsdpMessageFilter(predicate: ((SsdpMessage) -> Boolean)?)

    /**
     * Set a [predicate] to filter icons being downloaded for device. If not provided, all are downloaded
     */
    fun setIconFilter(predicate: ((Icon) -> Boolean)?): IControlPoint

    /**
     * Add a [listener] for device discovery
     */
    fun addDiscoveryListener(listener: DiscoveryListener)

    /**
     * Remove a [listener] for device discovery
     */
    fun removeDiscoveryListener(listener: DiscoveryListener)

    /**
     * Add an event [listener]
     */
    fun addEventListener(listener: EventListener)

    /**
     * Remove an event [listener]
     */
    fun removeEventListener(listener: EventListener)

    /**
     * Add a multicast event [listener]
     */
    fun addMulticastEventListener(listener: EventListener)

    /**
     * Remove a multicast event [listener]
     */
    fun removeMulticastEventListener(listener: EventListener)

    /**
     * Find device by UDN [udn] and
     * @return [Device] or null if not found.
     */
    fun getDevice(udn: String): Device?

    /**
     * Try to add a device by UDN [uuid] and URL [location].
     * This is used to read the cached information based on it.
     */
    fun tryAddDevice(uuid: String, location: String)

    /**
     * Try to add the pinned device by URL [location] (needs to be an accurate value).
     * After devices can be added, they will not be deleted by expiration or ByeBye.
     */
    fun tryAddPinnedDevice(location: String)

    /**
     * Remove the pinned device by URL [location]
     * Even if there is a device with the same location, it will not be deleted if it is not a pinned device.
     */
    fun removePinnedDevice(location: String)
}

interface EventListener

interface DiscoveryListener